The main tasks that the EffectBegin function should perform are:
The following code checks to see if the destination has changed since the last call to the EffectBegin function:
if (p->conditionFlags & (codecConditionNewClut+
codecConditionFirstFrame+codecConditionNewDepth+
codecConditionNewDestination+codecConditionNewTransform))
If this evaluates to true , the destination has changed. This expression checks a series of flags that are passed to the EffectBegin function in the conditionsFlags field of the decompressParams parameter. When the destination is changed, QuickTime sets these flags to alert the effect component to update its internal state.
The most important information that you need to store about the new destination is its base address and its rowBytes value. These values allow you to draw onto the destination surface.
Listing 22 shows an example function that stores information in the effect component's global data structure about the destination PixMap passed to the function.
Listing 22 Storing information about a new destination frame
static long BlitterSetDest(BlitGlobals*glob, // input: our globals
PixMap *dstPixMap, // input: pixels we will draw into
Rect *dstRect) // input: area of pixels we will draw into
{
OSErr result = noErr;
long offsetH,offsetV;
char *baseAddr;
// Calculate the based address according to the format of the
// destination PixMap
offsetH = (dstRect->left - dstPixMap->bounds.left);
if (dstPixMap->pixelSize == 16)
{
offsetH <<= 1; // 1 pixel = 2 bytes
}
else
{
if (dstPixMap->pixelSize == 32)
{
offsetH <<= 2; // 1 pixel = 4 bytes
}
else
{
result = -1; // this is a data format we can't handle
}
}
offsetV = (dstRect->top - dstPixMap->bounds.top)
* dstPixMap->rowBytes;
baseAddr = dstPixMap->baseAddr + offsetH + offsetV;
glob->dstBaseAddr = baseAddr;
glob->dstRowBytes = dstPixMap->rowBytes;
return result;
} // BlitterSetDest
The process for checking for new sources is broadly similar. The CodecDecompressParams data structure passed into the EffectBegin function has a field called majorSourceChangeSeed . This contains a seed number generated from the characteristics of the set of sources for the effect. If the sources change, the majorSourceChangeSeed value will also change, so the effect can store the current value in its global data structure and compare it to the current value. If they are different, the effect knows its sources have changed.
When the effect detects that one or more of its sources have changed, it must iterate through all its sources and reload information about them.
Listing 23 shows example code that performs these operations. Listing 24 shows the BlitterSetSource function that is called by this example code. The BlitterSetSource function is analogous to the BlitterSetDest function shown in Listing 22 .
Listing 23 Checking for source changes
// Check to see if one or more sources have changed
if (p->majorSourceChangeSeed != glob->majorSourceChangeSeed)
{
// grab start of input chain for this effect
source = effect->source;
// we can play with up to kMaxSources sources, so go get them
while (source != nil && numSources < kMaxSources)
{
// now give that source to our blitter
err = BlitterSetSource(glob, numSources, source);
if (err != noErr)
goto bail;
source = source->next;
++numSources;
}
}
Listing 24 Storing information about a new source frame
static long BlitterSetSource(BlitGlobals*glob, // input: our globals
long sourceNumber, // input: source index to set
CDSequenceDataSourcePtr source) // input: source value
{
OSErr err = noErr;
if (sourceNumber >= kMaxSources)
{
// too many sources for us to handle
return noErr;
}
else
{
// a source we can handle, save it away
err = RequestImageFormat(source, glob->width, glob->height,
glob->dstPixelFormat);
if (err == noErr)
{
glob->sources[sourceNumber].src = source;
}
else
{
glob->sources[sourceNumber].src = nil;
}
}
return (err);
} // BlitterSetSource
Listing 25 shows how to read the value of a non-tweened parameter. The QTFindChildByID function is used to retrieve the atom containing the parameter value. The parameter value is then copied from the atom using the function QTCopyAtomDataToPtr . If the value is successfully copied, it is endian-flipped to ensure it is in native-endian format (parameter values are always stored in big-endian format). If the copy failed, a default value is provided.
The value retrieved from the parameter is stored in the component's global data structure (called, in this example, global->blitter ). This allows the value to be used by other functions, notably the component's EffectRenderFrame function.
Listing 25 Reading a parameter value
{
Ptr data = p->data;
QTAtom atom;
QTAtomID atomID = 1;
long actSize;
// Find the 'sden' atom
atom = QTFindChildByID((QTAtomContainer) &data,
kParentAtomIsContainer,
OSTypeConst('sden'), // The name of the parameter
atomID, // The ID of the parameter
nil);
// Copy the parameter value from the atom
if (QTCopyAtomDataToPtr((QTAtomContainer) &data,
atom,
false,
sizeof(long),
&((glob->blitter).scratchDensity),
&actSize)!=noErr)
{
// If the copy failed, use a default value for this parameter
((glob->blitter).scratchDensity) = 1;
}
else
{
// Otherwise, the copy succeeded, so endian flip and store the
// parameter value
((glob->blitter).scratchDensity) =
EndianS32_BtoN(((glob->blitter).scratchDensity));
}
}
If the parameter value can contain a tweened value, you can use code similar to that shown in Listing 26 to retrieve the parameter value. The functions InitializeTweenGlobals and CreateTweenRecord are utility functions that Apple provides as part of the dimmer effect sample framework (see "The Sample Effect Component" ).
Listing 26 Reading a tweened parameter value
{
Ptr data = p->data;
OSErr err;
long index = 1;
err = InitializeTweenGlobals(&glob->tweenGlobals, p);
if (err!=noErr)
goto bail;
// Make our tweener, return if we already have it
err = CreateTweenRecord(&glob->tweenGlobals,
&glob->percentage,
OSTypeConst('pcnt'), // The name of the parameter
1, // The ID of the parameter
sizeof(Fixed),
kTweenTypeFixed,
(void*) 0,
(void*) fixed1,
effect->frameTime.virtualDuration);
if (err!=noErr)
goto bail;
glob->initialized = true;
}
If you have specified that one or more of your parameter's values can be tweened, you need to implement code to perform the tweening in the EffectBegin function.
Listing 27 shows an example of tweening a parameter value. The current frame time is retrieved and subtracted from the effect's virtualStartTime . This calculates how far through the execution of the current effect sequence we are, expressed as a percentage.
With this information, the code then calls QTDoTween to interpolate the parameter value, leaving the resulting value in glob->comp1Tween.tweenData .
Listing 27 Tweening parameter values
wide percentage;
// Find out how far through the effect we are
percentage = effect->frameTime.value;
CompSub(&effect->frameTime.virtualStartTime, &percentage);
// Tween our parameters and get the current value for this frame, prepare
// to render it when the EffectRenderFrame happens
{
Fixed thePercentage;
if (glob->percentage.tween)
QTDoTween(glob->percentage.tween, percentage.lo,
glob->percentage.tweenData, nil, nil, nil);
thePercentage = **(Fixed**) (glob->percentage.tweenData);
// If we are before the half-way point of this transition, we should
// be fading the first source to black
if (thePercentage < fixed1/2)
{
(glob->blitter).direction = 1;
(glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512), thePercentage));
}
// Otherwise, we are fading up onto the new source
else
{
(glob->blitter).direction = 0;
(glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512),
thePercentage)) - 255;
}
}
| Previous | Chapter Contents | Chapter Top | Next |